#include "StdAfx.h"
#include ".\helper.h"
#include <WindowsX.h>

int bits = 16;
DWORD initialtime=0;
int initcapture = 0;
int irsmallcount=0;
int nActualFrame=0;
int nCurrFrame=0;
float fRate=0.0;
float fActualRate=0.0;
float fTimeLength=0.0;

int recordstate=0;
int timelapse=50;

#ifdef LOG4CPLUSLOGGER
log4cplus::Logger logger_ = log4cplus::Logger::getInstance( "ScreenRecord" );
#endif

static HANDLE  Bitmap2Dib( HBITMAP hbitmap, UINT bits )
{
	HANDLE               hdib ;
	HDC                 hdc ;
	BITMAP              bitmap ;
	UINT                wLineLen ;
	DWORD               dwSize ;
	DWORD               wColSize ;
	LPBITMAPINFOHEADER  lpbi ;
	LPBYTE              lpBits ;

	GetObject(hbitmap,sizeof(BITMAP),&bitmap) ;

	//
	// DWORD align the width of the DIB
	// Figure out the size of the colour table
	// Calculate the size of the DIB
	//
	wLineLen = (bitmap.bmWidth*bits+31)/32 * 4;
	wColSize = sizeof(RGBQUAD)*((bits <= 8) ? 1<<bits : 0);
	dwSize = sizeof(BITMAPINFOHEADER) + wColSize +
		(DWORD)(UINT)wLineLen*(DWORD)(UINT)bitmap.bmHeight;

	//
	// Allocate room for a DIB and set the LPBI fields
	//
	hdib = GlobalAlloc(GHND,dwSize);
	if (!hdib)
		return hdib ;

	lpbi = (LPBITMAPINFOHEADER)GlobalLock(hdib) ;

	lpbi->biSize = sizeof(BITMAPINFOHEADER) ;
	lpbi->biWidth = bitmap.bmWidth ;
	lpbi->biHeight = bitmap.bmHeight ;
	lpbi->biPlanes = 1 ;
	lpbi->biBitCount = (WORD) bits ;
	lpbi->biCompression = BI_RGB ;
	lpbi->biSizeImage = dwSize - sizeof(BITMAPINFOHEADER) - wColSize ;
	lpbi->biXPelsPerMeter = 0 ;
	lpbi->biYPelsPerMeter = 0 ;
	lpbi->biClrUsed = (bits <= 8) ? 1<<bits : 0;
	lpbi->biClrImportant = 0 ;

	//
	// Get the bits from the bitmap and stuff them after the LPBI
	//
	lpBits = (LPBYTE)(lpbi+1)+wColSize ;

	hdc = CreateCompatibleDC(NULL) ;

	GetDIBits(hdc,hbitmap,0,bitmap.bmHeight,lpBits,(LPBITMAPINFO)lpbi, DIB_RGB_COLORS);

	lpbi->biClrUsed = (bits <= 8) ? 1<<bits : 0;

	DeleteDC(hdc) ;
	GlobalUnlock(hdib);

	return hdib ;
}

LPBITMAPINFOHEADER captureScreenFrame(int left,int top,int width, int height,int tempDisableRect)
{
	CURSORINFO cursourInfo;
	HDC hScreenDC = ::GetDC(NULL);

	HDC hMemDC = ::CreateCompatibleDC(hScreenDC);     
	HBITMAP hbm;

	hbm = CreateCompatibleBitmap(hScreenDC, width, height);
	HBITMAP oldbm = (HBITMAP) SelectObject(hMemDC, hbm);	 
	BitBlt(hMemDC, 0, 0, width, height, hScreenDC, left, top, SRCCOPY);	 	

	//Get Cursor Pos
	ZeroMemory(&cursourInfo, sizeof(cursourInfo));
	cursourInfo.cbSize = sizeof(cursourInfo);
	GetCursorInfo(&cursourInfo);
	HCURSOR hcur = cursourInfo.hCursor;
	POINT xPoint = cursourInfo.ptScreenPos;
	xPoint.x-=left;
	xPoint.y-=top;

	//Draw the HighLight	
// 	if (g_highlightcursor==1) {	
// 
// 		POINT highlightPoint; 		
// 
// 		highlightPoint.x = xPoint.x -64 ;
// 		highlightPoint.y = xPoint.y -64 ;		
// 
// 		InsertHighLight( hMemDC, highlightPoint.x, highlightPoint.y);
// 
// 	}

	//Draw the Cursor	
//	if (g_recordcursor==1)
	{



		ICONINFO iconinfo ;	
		BOOL ret;
		ret	= GetIconInfo( hcur,  &iconinfo ); 
		if (ret) {

			xPoint.x -= iconinfo.xHotspot;
			xPoint.y -= iconinfo.yHotspot;

			//need to delete the hbmMask and hbmColor bitmaps
			//otherwise the program will crash after a while after running out of resource
			if (iconinfo.hbmMask) DeleteObject(iconinfo.hbmMask);
			if (iconinfo.hbmColor) DeleteObject(iconinfo.hbmColor);

		}		


		::DrawIcon( hMemDC,  xPoint.x,  xPoint.y, hcur); 							

	}

	SelectObject(hMemDC,oldbm);    			
	LPBITMAPINFOHEADER pBM_HEADER = (LPBITMAPINFOHEADER)GlobalLock(Bitmap2Dib(hbm, bits));	
	//LPBITMAPINFOHEADER pBM_HEADER = (LPBITMAPINFOHEADER)GlobalLock(Bitmap2Dib(hbm, 24));	
	if (pBM_HEADER == NULL) { 

		MessageBox(NULL,"Error reading a frame!","Error",MB_OK | MB_ICONEXCLAMATION);					
		exit(1);
	}    

	DeleteObject(hbm);			
	DeleteDC(hMemDC);	

	ReleaseDC(NULL,hScreenDC) ;	

	return pBM_HEADER;
}


void FreeFrame(LPBITMAPINFOHEADER alpbi)
{

	if (!alpbi)
		return ;

	GlobalFreePtr(alpbi);
	//GlobalFree(alpbi);
	alpbi = NULL;
}



UINT RecordAVIThread(LPVOID pParam) 
{
	LOG_TRACE("RecordAVIThread" << pParam);
	CHelper *helper = (CHelper*)pParam;
	helper->RecordProc();
	return 0;
}
LOG_CLASS_IMPLEMENT(CHelper);
CHelper::CHelper(void)
{
	m_hWndGlobal = 0;
	m_nCompressor =0;
	m_pCompressorInfo = NULL;
}

CHelper::~CHelper(void)
{
}

int CHelper::GetScreenRect(void)
{
	HDC hScreenDC = ::GetDC(NULL);	
	m_nWidth = GetDeviceCaps(hScreenDC,HORZRES);
	m_nHeight = GetDeviceCaps(hScreenDC,VERTRES);		
	::ReleaseDC(NULL,hScreenDC);

	LOG_TRACE("m_nWidth=" << m_nWidth << ", m_nHeight=" << m_nHeight);

	return 0;
}

int CHelper::Init(const CString& strTempPath)
{
	LOG_TRACE("Init:" << strTempPath);
	m_strTempPath = strTempPath;
	GetScreenRect();
	InitOption();
	return 0;
}

int CHelper::Record()
{
	LOG_TRACE("Record");
	recordstate = 1;
	CWinThread * pThread = AfxBeginThread(RecordAVIThread, this);	
// 	Sleep(5000);

	return 0;
}

int CHelper::StopRecord()
{
	recordstate = 0;
	return 0;
}

int CHelper::InitOption()
{
	LOG_TRACE("InitOption");
	LPBITMAPINFOHEADER first_alpbi =  captureScreenFrame(0, 0, m_nWidth, m_nHeight, 1);		

	if  (m_pCompressorInfo == NULL) {
		m_pCompressorInfo = (ICINFO *) calloc(30,sizeof(ICINFO));
	}
	else {
		free(m_pCompressorInfo);
		m_pCompressorInfo = (ICINFO *) calloc(30,sizeof(ICINFO));
	}	

	for(int i=0; ICInfo(ICTYPE_VIDEO, i, &m_pCompressorInfo[m_nCompressor]); i++) {
		if (m_nCompressor>=30) break; //maximum allows 30 compressors		

		HIC hic = ICOpen(m_pCompressorInfo[m_nCompressor].fccType, m_pCompressorInfo[m_nCompressor].fccHandler, ICMODE_QUERY);
		if (hic) {
			if   (ICERR_OK==ICCompressQuery(hic, first_alpbi, NULL)) {
				ICGetInfo(hic, &m_pCompressorInfo[m_nCompressor], sizeof(ICINFO));				
				m_nCompressor ++ ;				
			}
			ICClose(hic);
		}
	}


	FreeFrame(first_alpbi);

	m_nFPS = 20;

	return 0;
}

int CHelper::RecordProc()
{
	LOG_TRACE("enter RecordProc");
	PAVIFILE pfile = NULL;
	AVISTREAMINFO strhdr;
	LPBITMAPINFOHEADER alpbi = NULL;
	CString strFilePath = m_strTempPath + _T("\\aaa.avi");
	LOG_TRACE("ProcordProc " << strFilePath);
	LPCTSTR pszFileName = (LPCTSTR)( strFilePath );
	AVICOMPRESSOPTIONS opts;
	AVICOMPRESSOPTIONS FAR * aopts[1] = {&opts};
	PAVISTREAM ps = NULL, psCompressed = NULL;

	alpbi=captureScreenFrame(0, 0, m_nWidth, m_nHeight, 1);
	////////////////////////////////////////////////
	// INIT AVI USING FIRST FRAME
	////////////////////////////////////////////////
	AVIFileInit();

	//
	// Open the movie file for writing....
	//
//	strcpy(szTitle,"AVI Movie");
	 
	HRESULT hr = AVIFileOpen(&pfile, pszFileName, OF_WRITE | OF_CREATE, NULL);	
	if (hr != AVIERR_OK) goto error;


	// Fill in the header for the video stream....
	// The video stream will run in 15ths of a second....
	_fmemset(&strhdr, 0, sizeof(strhdr));
	strhdr.fccType                = streamtypeVIDEO;// stream type
	
	//strhdr.fccHandler             = compfccHandler;
	strhdr.fccHandler             = 0;
	
	strhdr.dwScale                = 1;
	strhdr.dwRate                 = m_nFPS;		    
	strhdr.dwSuggestedBufferSize  = alpbi->biSizeImage;
	SetRect(&strhdr.rcFrame, 0, 0,		    // rectangle for stream
	    (int) alpbi->biWidth,
	    (int) alpbi->biHeight);

	// And create the stream;
	hr = AVIFileCreateStream(pfile,	&ps, &strhdr);
	if (hr != AVIERR_OK) 	goto error; 
	
	
	memset(&opts, 0, sizeof(opts)); 
	aopts[0]->fccType			 = streamtypeVIDEO;
	aopts[0]->fccHandler		 = mmioFOURCC('M', 'S', 'V', 'C');
	//aopts[0]->fccHandler		 = compfccHandler;
	aopts[0]->dwKeyFrameEvery	   = 20/*keyFramesEvery*/;		// keyframe rate 
	aopts[0]->dwQuality		 = 6500/*compquality*/;        // compress quality 0-10,000 
	aopts[0]->dwBytesPerSecond	         = 0;		// bytes per second 
	aopts[0]->dwFlags			 = AVICOMPRESSF_VALID | AVICOMPRESSF_KEYFRAMES;    // flags 		
	aopts[0]->lpFormat			 = 0x0;                         // save format 
	aopts[0]->cbFormat			 = 0;
	aopts[0]->dwInterleaveEvery = 0;			// for non-video streams only 



	//The 1 here indicates only 1 stream
	//if (!AVISaveOptions(NULL, 0, 1, &ps, (LPAVICOMPRESSOPTIONS *) &aopts)) 
	//        goto error;
	

	hr = AVIMakeCompressedStream(&psCompressed, ps, &opts, NULL);
	if (hr != AVIERR_OK)  	goto error; 

	hr = AVIStreamSetFormat(psCompressed, 0, 
			       alpbi,	    // stream format
			       alpbi->biSize +   // format size
			       alpbi->biClrUsed * sizeof(RGBQUAD));
	if (hr != AVIERR_OK) goto error;	

	
 	FreeFrame(alpbi);	
	alpbi=NULL;
	

	DWORD timeexpended, frametime, oldframetime;		
	initialtime = timeGetTime();	
	initcapture = 1;	
	oldframetime = 0;

	nCurrFrame = 0;
	nActualFrame = 0;

	
	//////////////////////////////////////////////
	// WRITING FRAMES
	//////////////////////////////////////////////	

	long divx, oldsec;
	divx=0;
	oldsec=0;
	while (recordstate) {  //repeatedly loop

		if (initcapture==0) {			 
			timeexpended = timeGetTime() - initialtime;
		}
		else {
			
			frametime = 0;
			timeexpended = 0;			
			
		}		

		alpbi=captureScreenFrame(0,0,m_nWidth, m_nHeight,0);		
		
		if (initcapture==0) {

			if (timelapse>1000)
				frametime++;
			else
				frametime = (DWORD) (((double) timeexpended /1000.0 ) * (double) (1000.0/timelapse));

		}
		else 
			initcapture = 0;
		
		fTimeLength = ((float) timeexpended) /((float) 1000.0);
		
		
		if ((frametime==0) || (frametime>oldframetime)) { 


			//if frametime repeats...the avistreamwrite will cause an error
			hr = AVIStreamWrite(psCompressed,	// stream pointer
				frametime,				// time of this frame
				1,				// number to write
				(LPBYTE) alpbi +		// pointer to data
					alpbi->biSize +
					alpbi->biClrUsed * sizeof(RGBQUAD),
					alpbi->biSizeImage,	// size of this frame
				//AVIIF_KEYFRAME,			 // flags....
				0,    //Dependent n previous frame, not key frame
				NULL,
				NULL);
			if (hr != AVIERR_OK)
				break;		

			nActualFrame ++ ;
			nCurrFrame = frametime;
			fRate = ((float) nCurrFrame)/fTimeLength;						
			fActualRate = ((float) nActualFrame)/fTimeLength;
			
			//Update recording stats every half a second
			divx = timeexpended / 500;

// 			if (divx != oldsec) {				
// 				oldsec=divx;
// 				InvalidateRect(m_hWndGlobal,NULL, FALSE);				
// 			}			
			
			//free memory
			FreeFrame(alpbi);
			alpbi=NULL;

			oldframetime = frametime;


		} // if frametime is different


		
		//Version 1.1
		int haveslept = 0;
		int pausecounter = 0;
		int pauseremainder = 0;
		int pauseindicator = 1;
		DWORD timestartpause;
		DWORD timeendpause;
		DWORD timedurationpause;		
// 		while (recordpaused) {			
// 		
// 			if (!haveslept) timestartpause = timeGetTime();
// 		
// 			//Flash Pause Indicator in Title Bar
// 			pausecounter++;
// 			if ((pausecounter % 8)==0) { 
// 				
// 				//if after every 400 milliseconds (8 * 50)				
// 				if (pauseindicator) {
// 					SetTitleBar("");
// 					pauseindicator = 0;
// 				}
// 				else {
// 					SetTitleBar("Paused");
// 					pauseindicator = 1;
// 				}							
// 
// 			}
// 
// 			//do nothing.. wait
// 			::Sleep(50); //Sleep for 50 			
// 			
// 			haveslept=1;
// 
// 		}

		//Version 1.1
		if (haveslept) {

			timeendpause = timeGetTime();
			timedurationpause =  timeendpause - timestartpause;

			//CString msgstr;
			//msgstr.Format("timestartpause %ld\ntimeendpause %ld\ntimedurationpause %ld",timeendpause,timeendpause,timedurationpause);
			//MessageBox(NULL,msgstr,"Note",MB_OK);

			initialtime = initialtime + timedurationpause;

		}
		else  {
		
			//introduce time lapse
			//maximum lapse when recordstate changes will be less than 100 milliseconds
			int no_iteration = timelapse/50;
			int remainlapse = timelapse - no_iteration*50;		 
			for (int j=0;j<no_iteration;j++)		
			{
				::Sleep(50); //Sleep for 50 milliseconds many times
				if (recordstate==0) break;
			}		
			if (recordstate==1) Sleep(remainlapse);

		}
		

	} //for loop
	
	
error:	
	
	//
	// Now close the file
	//		

	AVISaveOptionsFree(1,(LPAVICOMPRESSOPTIONS FAR *) &aopts);	



	if (pfile)
		AVIFileClose(pfile);
	
	
	if (ps)
		AVIStreamClose(ps);
	
	
	if (psCompressed)
		AVIStreamClose(psCompressed);

	AVIFileExit();	

	
// 	if (hr != NOERROR) 	{
// 		
// 		PostMessage(m_hWndGlobal,WM_USER_RECORDINTERRUPTED,0,0);
// 
// 		/*
// 		char *ErrorBuffer; // This really is a pointer - not reserved space!
// 		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 	FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPTSTR)&ErrorBuffer, 0, NULL);
// 
// 		CString reasonstr(ErrorBuffer);
// 		CString errorstr("File Creation Error. Unable to rename file.\n\n");
// 		CString reportstr;
// 
// 		reportstr = errorstr + reasonstr;
// 		MessageBox(NULL,reportstr,"Note",MB_OK | MB_ICONEXCLAMATION);
// 		*/
// 		
// 		
// 		if (compfccHandler != mmioFOURCC('M', 'S', 'V', 'C'))	{
// 
// 			if (IDYES == MessageBox(NULL, "Error recording AVI file using current compressor. Use default compressor ? ", "Note", MB_YESNO | MB_ICONEXCLAMATION)) {
// 
// 				compfccHandler = mmioFOURCC('M', 'S', 'V', 'C');
// 				strCodec = "Default Compressor";
// 				PostMessage(hWndGlobal,WM_USER_RECORDSTART,0,0);
// 			}			
// 
// 		}
// 		else
// 			MessageBox(NULL, "Error Creating AVI File", "Error", MB_OK | MB_ICONEXCLAMATION);			
// 
// 
// 		
// 		return 0;
// 
// 	}


	//Save the file on success

	void Save();
//	PostMessage(hWndGlobal,WM_USER_GENERIC,0,0);	

	return 0;
}

